package com.saurshaz.test.roo.trading.services;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;

import org.springframework.stereotype.Service;

import com.saurshaz.test.roo.trading.SecurityTrade;
import com.saurshaz.test.roo.trading.SecurityType;
import com.saurshaz.test.roo.trading.TradeType;
import com.saurshaz.test.roo.trading.bo.FinancialInstrument;
import com.saurshaz.test.roo.trading.bo.FinancialInstrumentComparator;
import com.saurshaz.test.roo.trading.controllers.backingforms.SecurityTradeForm;

@Service(value = "securityTradeService")
public class SecurityTradingServiceImpl implements SecurityTradingService {

	@Override
	public List<SecurityTrade> getAllSecurities() {
		return SecurityTrade.findAllSecurityTrades();
	}

	@Override
	public SecurityTrade createSecurity(SecurityTradeForm securityTradeForm) {
		SecurityTrade securityTrade = new SecurityTrade();

		TradeType tradeType = TradeType.findTradeType(Long
				.parseLong(securityTradeForm.getTransactionType()));
		SecurityType securityType = SecurityType.findSecurityType(Long
				.parseLong(securityTradeForm.getSecurityType()));
		Double tradeValue = tradeType.getEffect()
				* securityTradeForm.getQuantity()
				* securityTradeForm.getPrice();// calculate and set correct
												// market value

		securityTrade.setPrice(securityTradeForm.getPrice());
		securityTrade.setSecurityTypeId(securityType);
		securityTrade.setTradeTypeId(tradeType);
		securityTrade.setTradedVolume(securityTradeForm.getQuantity()
				.longValue());
		securityTrade.setTradeValue(tradeValue);

		System.out.println(securityTrade);

		securityTrade.persist();

		return securityTrade;
	}

	@Override
	public String getMarketValue(String quantity, String price,
			String tradeTypeId) {

		TradeType tradeType = TradeType.findTradeType(Long
				.parseLong(tradeTypeId));
		Double tradeValue = tradeType.getEffect() * Double.parseDouble(price)
				* Double.parseDouble(quantity);// calculate and set correct
												// market value

		return tradeValue.toString();// format it properly
	}


	private void adjustQueueOnTransaction(Queue<FinancialInstrument> queue,
			FinancialInstrument financialInstrumentNew, Double priceIncrease) {

		// get the current financial transaction quantity and price
		Double currentPrice = financialInstrumentNew.getPrice();
		Integer currentQuantity = financialInstrumentNew.getQuantity();

		FinancialInstrument baseFi = queue.poll();
		if (baseFi != null) {

			if (currentQuantity > baseFi.getQuantity()) {
				// more iterations of his method needed for adjustments

				// adjust the current Financial Instrument quantity
				FinancialInstrument financialInstrumentMore = new FinancialInstrument();
				financialInstrumentMore.setQuantity(currentQuantity
						- baseFi.getQuantity());

				// calculate P&L for this transaction
				priceIncrease = priceIncrease
						+ (baseFi.getPrice() - currentPrice.longValue());

				financialInstrumentNew.setProfit(priceIncrease
						* financialInstrumentNew.getQuantity());

				// recurse into this function
				adjustQueueOnTransaction(queue, financialInstrumentMore,
						priceIncrease);

			} else if (baseFi.getQuantity() > currentQuantity) {
				// adjust the queue with putting back the same financial
				// instrument with lesser quantity
				baseFi.setQuantity(baseFi.getQuantity() - currentQuantity);
				queue.offer(baseFi);

				// calculate P&L for this transaction
				priceIncrease = priceIncrease
						+ (currentPrice.longValue() - baseFi.getPrice());
				financialInstrumentNew.setProfit(priceIncrease
						* financialInstrumentNew.getQuantity());

			} else {
				// equal quantities perfect match
				// only profit/loss calculation needed
				// calculate P&L for this transaction
				priceIncrease = priceIncrease
						+ (currentPrice.longValue() - baseFi.getPrice());
				financialInstrumentNew.setProfit(priceIncrease
						* financialInstrumentNew.getQuantity());

			}

			// update fi queue with profit information for new sold security
			FinancialInstrument fi = queue.poll();
			queue.offer(fi);

		} else {
			// queue is empty
			// cant't proceed with sell, buy can happen -- HANDLE THIS TODO
			System.out.println("can't sell .. only buy");
		}
	}

	// called when a particular transaction is one for an identified security
	// like INFY, WIP
	public Map<String, Queue<FinancialInstrument>> updateSecuritiesCollection(
			SecurityTrade securityTrade,
			Map<String, Queue<FinancialInstrument>> mapOfQueues) {

		String key = securityTrade.getSecurityTypeId().getSymbol();
		Queue<FinancialInstrument> queue = mapOfQueues.get(key);

		if (queue == null) {
			queue = new PriorityQueue<FinancialInstrument>(10,
					new FinancialInstrumentComparator());
		}

		// create and insert new FInancial Instrument to queue collection for
		// this transaction(current price details)
		FinancialInstrument financialInstrumentNew = new FinancialInstrument();
		financialInstrumentNew.setId(securityTrade.getId());
		financialInstrumentNew.setPrice(securityTrade.getPrice());
		financialInstrumentNew.setQuantity(securityTrade.getTradedVolume()
				.intValue());
		financialInstrumentNew.setSymbol(securityTrade.getSecurityTypeId()
				.getSymbol());

		// add f.i. to queue
		queue.offer(financialInstrumentNew);

		// calculate profit or loss and adjust the queue accordingly, only at
		// time of sale
		if (securityTrade.getTradeTypeId().getCode().equalsIgnoreCase("SEL")) {
			adjustQueueOnTransaction(queue, financialInstrumentNew, new Double(
					0));
		}

		// update map with updated queue
		mapOfQueues.put(key, queue);

		return mapOfQueues;
	}

	public Map<String, Queue<FinancialInstrument>> getSecuritiesCollection() {
		Map<String, Queue<FinancialInstrument>> mapOfQueues = new HashMap<String, Queue<FinancialInstrument>>();
		return mapOfQueues;

	}

}
